package com.taobao.common.dao.persistence;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.ibatis.SqlMapClientTemplate;

import com.taobao.common.dao.persistence.exception.DAOException;
import com.taobao.common.dao.persistence.exception.DAOOutMaxSizeException;
import com.taobao.common.dao.persistence.exception.DBRouterException;
import com.taobao.common.dao.query.QueryBase;

/**
 * @author xingdian
 */
public class SqlMapBaseDAO {
  private static final int        MAX_IDS_SIZE                = 100;
  protected static final int      PAGE_SIZE                   = 4;
  protected static final String   ALL_DEFAULT_DB_NAME         = "all";
  protected static final Object[] NO_ARGUMENTS                = new Object[0];
  private static Integer          DEFAULT_MAX_RESULT_SET_SIZE = new Integer(10000);                // 能够返回的记录集合最大值的缺省值
  protected Log                   log                         = LogFactory.getLog(this.getClass());
  private SqlMapBaseDAOSupport    sqlDaoBaseSupport;
  private Integer                 maxResultSetSize            = DEFAULT_MAX_RESULT_SET_SIZE;       // 能够返回的记录集合最大值

  /**
   * 将id数组拼成 1,2,3 这样的形式
   *
   * @param ids
   *
   * @return
   */
  protected String ids2SQLInString(Collection ids) {
    if ((ids == null) || ids.isEmpty()) {
      return "";
    }

    StringBuffer sb = new StringBuffer();

    for (Iterator it = ids.iterator(); it.hasNext();) {
      sb.append(it.next()).append(',');
    }

    sb.deleteCharAt(sb.length() - 1);

    return sb.toString();
  }

  /**
   * @return Returns the maxResultSetSize.
   */
  public Integer getMaxResultSetSize() {
    return maxResultSetSize;
  }

  /**
   * @param maxResultSetSize
   *          The maxResultSetSize to set.
   */
  public void setMaxResultSetSize(Integer maxResultSetSize) {
    this.maxResultSetSize = maxResultSetSize;
  }

  /**
   * @return Returns the sqlDaoBaseSupport.
   */
  public SqlMapBaseDAOSupport getSqlDaoBaseSupport() {
    return sqlDaoBaseSupport;
  }

  /**
   * @param sqlDaoBaseSupport
   *          The sqlDaoBaseSupport to set.
   */
  public void setSqlDaoBaseSupport(SqlMapBaseDAOSupport sqlDaoBaseSupport) {
    this.sqlDaoBaseSupport = sqlDaoBaseSupport;
  }

  /**
   * 用于调试SQL语句的执行时间.
   */
  private void logRunTime(String statementName, String dbName, long runTime) {
    if (log.isDebugEnabled()) {
      log.debug("Sql " + statementName + " executed on " + dbName + " databases. Run time estimated: " + runTime + "ms");
    }
  }

  /**
   * 获得底层的数据库连接
   *
   * @param dbRoute
   *
   * @return 数据库连接
   *
   * @throws DAOException
   */
  public final Connection getConnection(DBRoute dbRoute) throws DAOException {
    if (dbRoute == null) {
      throw new DAOException("DBRoute 不能为空。");
    }

    Map dbMap = this.getSqlMapClient(dbRoute, null);

    if (dbMap.size() != 1) {
      throw new DAOException("不能唯一确定数据库！");
    } else {
      Iterator it = dbMap.values().iterator();

      while (it.hasNext()) {
        SqlMapClientTemplate client = (SqlMapClientTemplate) it.next();

        try {
          return client.getDataSource().getConnection();
        } catch (SQLException e) {
          throw new DAOException("不能获得数据库连接。");
        }
      }
    }

    return null;
  }

  /**
   * 根据路由规则返回特定的数据库模板
   */
  private Map getSqlMapClient(DBRoute dr, String sqlId) throws DAOException {
    return sqlDaoBaseSupport.getSqlMapTemplates(dr, sqlId);
  }

  private SqlMapClientTemplate getSqlMapTemplate(DBRoute dr) throws DBRouterException {
    return sqlDaoBaseSupport.getSqlMapTemplate(dr);
  }

  /**
   * 根据路由信息返回数据库的名字。
   * 
   * @param dr
   * @return
   * @throws DBRouterException
   */
  public String getDBName(DBRoute dr) throws DBRouterException {
    return sqlDaoBaseSupport.getDBName(dr, null);
  }

  protected Object executeQueryForObject(String statementName, Object parameterObject, DBRoute dr) throws DAOException {
    return executeQueryForObject(statementName, parameterObject, dr, true);
  }

  /**
   * 查找单个对象，可以统计记录的个数
   *
   * @param statementName
   * @param parameterObject
   * @param qualification
   * @param isExistsExit
   *          如果为真，则不在多库之间遍历，有值即返回。
   *
   * @return
   *
   * @throws DAOException
   */
  protected Object executeQueryForObject(String statementName, Object parameterObject, DBRoute dr, boolean isExistsExit) throws DAOException {
    Map dbList = getSqlMapClient(dr, statementName);
    int iTotalCount = 0;
    boolean isInteger = false;

    Iterator it = dbList.keySet().iterator();

    try {
      while (it.hasNext()) {
        String dbName = (String) it.next();
        SqlMapClientTemplate st = (SqlMapClientTemplate) dbList.get(dbName);
        long startTime = System.currentTimeMillis();
        Object returnObject = st.queryForObject(statementName, parameterObject);

        long endTime = System.currentTimeMillis();

        logRunTime(statementName, dbName, endTime - startTime);

        // 如果有值被返回
        if (returnObject != null) {
          // 如果查到了DO的对象
          if (BaseDO.class.isAssignableFrom(returnObject.getClass())) {
            ((BaseDO) returnObject).setDbRoute(new DBRoute(dbName));

            return returnObject;
          }

          // 如果查到记录的个数
          if (returnObject instanceof Integer) {
            iTotalCount += ((Integer) returnObject).intValue();
            isInteger = true;

            if ((iTotalCount > 0) && isExistsExit) {
              break;
            }
          } else {
            return returnObject;
          }
        } // end if
      } // while
    } catch (DataAccessException dae) {
      throw new DAOException(dae);
    }

    return (isInteger ? new Integer(iTotalCount) : null);
  }

  /**
   * 返回查询列表，如果不能唯一决定一个数据库，将在多库之间分别查询
   *
   * @param statementName
   * @param parameterObject
   * @param dr
   *
   * @return
   *
   * @throws DAOException
   */
  protected List executeQueryForList(String statementName, Object parameterObject, DBRoute dr) throws DAOException {
    Map dbMap = null;
    Iterator it = null;
    List resultList = null;

    dbMap = getSqlMapClient(dr, statementName);
    it = dbMap.keySet().iterator();
    resultList = new ArrayList();

    try {
      while (it.hasNext()) {
        long startTime = System.currentTimeMillis();
        String dbName = (String) it.next();
        SqlMapClientTemplate st = (SqlMapClientTemplate) dbMap.get(dbName);
        List list = st.queryForList(statementName, parameterObject);

        genDBRoute(dbName, list);

        long endTime = System.currentTimeMillis();

        logRunTime(statementName, dbName, endTime - startTime);
        resultList.addAll(list);
      }
    } catch (DataAccessException dae) {
      throw new DAOException(dae);
    }

    return resultList;
  }

  /**
   * 返回一个MAP结果集，KEY是返回DO中的一个字段名称
   *
   * @param statementName
   * @param parameterObject
   * @param dr
   * @param key
   *
   * @return
   *
   * @throws DAOException
   */
  protected Map executeQueryForMap(String statementName, Object parameterObject, DBRoute dr, String key) throws DAOException {
    Map dbMap = null;
    Iterator it = null;
    Map results = null;

    dbMap = getSqlMapClient(dr, statementName);
    it = dbMap.keySet().iterator();
    results = new HashMap();

    try {
      while (it.hasNext()) {
        long startTime = System.currentTimeMillis();
        String dbName = (String) it.next();
        SqlMapClientTemplate st = (SqlMapClientTemplate) dbMap.get(dbName);

        Map resultMap = st.queryForMap(statementName, parameterObject, key);

        genDBRoute(dbName, resultMap);

        long endTime = System.currentTimeMillis();

        logRunTime(statementName, dbName, endTime - startTime);
        results.putAll(resultMap);
      }
    } catch (DataAccessException dae) {
      throw new DAOException(dae);
    }

    return results;
  }

  /**
   * 更新数据库，可以插入一条记录，也可以删除一条记录 返回受影响的条数
   *
   * @param statementName
   * @param parameterObject
   * @param dr
   *
   * @return 被更新的记录数
   *
   * @throws DAOException
   */
  protected int executeUpdate(String statementName, Object parameterObject, DBRoute dr) throws DAOException {
    Map dbMap = getSqlMapClient(dr, statementName);

    if (dbMap.size() != 1) {
      throw new DAOException("不能确定数据库！");
    }

    Iterator it = dbMap.keySet().iterator();
    int updateRows = 0;

    try {
      while (it.hasNext()) {
        String dbName = (String) it.next();
        SqlMapClientTemplate st = (SqlMapClientTemplate) dbMap.get(dbName);
        long startTime = System.currentTimeMillis();

        updateRows = st.update(statementName, parameterObject);

        long endTime = System.currentTimeMillis();

        logRunTime(statementName, dbName, endTime - startTime);
      }

      return updateRows;
    } catch (DataAccessException dae) {
      throw new DAOException(dae);
    }
  }

  /**
   * 插入一条记录
   *
   * @param statementName
   * @param parameterObject
   * @param dr
   *
   * @return 新增加的记录主键
   *
   * @throws DAOException
   */
  protected Object executeInsert(String statementName, Object parameterObject, DBRoute dr) throws DAOException {
    Map dbMap = getSqlMapClient(dr, statementName);

    if (dbMap.size() != 1) {
      throw new DAOException("不能确定数据库！");
    }

    Iterator it = dbMap.keySet().iterator();
    Object back = null;

    try {
      while (it.hasNext()) {
        String dbName = (String) it.next();
        SqlMapClientTemplate st = (SqlMapClientTemplate) dbMap.get(dbName);
        long startTime = System.currentTimeMillis();

        // 调用Spring template
        back = st.insert(statementName, parameterObject);

        long endTime = System.currentTimeMillis();

        logRunTime(statementName, dbName, endTime - startTime);
      }

      return back;
    } catch (DataAccessException dae) {
      throw new DAOException(dae);
    }
  }

  /**
   * 多数据库分页查询. 注意：select SQL 和 count SQL 必须在mckinley-commom-config工程中的：
   * conf/bean/persistence.xml 文件中配置，并且配置必须相同.
   * 如果返回的DO实现了java.lang.Comparable，则对结果集进行排续. 注意：sql语句的排序必须和DO排序一至. 参考:
   * com.mckinley.biz.dal.member.dataobject.Shop (DO)
   * com.mckinley.biz.dal.member.ibatis.IbatisShopDAO.loadShops() (DAO实现)
   *
   * @param selelctStatementName
   *          "select .. from ... where ... order by ..."sql语句的mapping
   * @param countStatementName
   *          "select count() from ... where ..."sql语句的mapping
   * @param query
   *          查询参数
   *
   * @return DO集合
   *
   * @throws DAOException
   *           如果查询的结果集（所有数据库记录总和）大于maxResultSetSize， 则抛出DAOOutMaxSizeException
   *           缺省的maxResultSetSize见 DEFAULT_MAX_RESULT_SET_SIZE，
   *           如果需要配置，比如将此数值改为5000，则在DAO配置中(mckinley-common-config工程中src/
   *           conf/mckinley/bean/biz-dao.xml文件), 增加 "maxResultSetSize" 5000
   */
  protected List getQueryMergeList(String selelctStatementName, String countStatementName, QueryBase query) throws DAOException {
    if (selelctStatementName == null) {
      throw new NullPointerException("selelctStatementName can't be null.");
    }

    if (countStatementName == null) {
      throw new NullPointerException("countStatementName can't be null.");
    }

    if (query == null) {
      throw new NullPointerException("query can't be null.");
    }

    // 得到每个数据库的记录数
    Map dbMap = getSqlMapClient(query.getDbRoute(), countStatementName); // count

    // 语句得到的dbmap
    int dbCountSTSize = dbMap.size();

    if (dbCountSTSize == 0) {
      throw new IllegalStateException("dbroute config error in count statement :" + countStatementName);
    }

    Iterator it = dbMap.keySet().iterator();
    final Map countResult = new LinkedHashMap(dbCountSTSize);
    int totalCount = 0;

    while (it.hasNext()) {
      String dbName = (String) it.next();
      DBRoute dbRoute = new DBRoute();

      dbRoute.setXid(dbName);

      // SqlMapClientTemplate st = (SqlMapClientTemplate)
      // dbMap.get(dbName);
      Integer count = (Integer) this.executeQueryForObject(countStatementName, query, dbRoute);

      countResult.put(dbName, count);
      totalCount += count.intValue();
    }

    // 对记录数大小排序
    List keys = new ArrayList();

    keys.addAll(countResult.keySet());
    Collections.sort(keys, new Comparator() {
      public int compare(Object arg0, Object arg1) {
        String thisname = (String) arg0;
        String thatname = (String) arg1;
        Integer thiscount = (Integer) countResult.get(thisname);
        Integer thatcount = (Integer) countResult.get(thatname);

        return thiscount.compareTo(thatcount);
      }
    });

    if (log.isDebugEnabled()) {
      log.debug("result count:" + totalCount);
    }

    // 如果记录总数大于规定值，不再继续执行
    if (this.getMaxResultSetSize().intValue() < totalCount) {
      throw new DAOOutMaxSizeException("获取的记录数:" + totalCount + "太大,应小于" + this.getMaxResultSetSize());
    }

    query.setTotalItem(new Integer(totalCount));

    // alahan modify start
    // 多个数据库整除后余下条数
    int iOffSet = query.getPageSize().intValue() % dbCountSTSize;

    // 每个数据库需要获取的记录数
    int iPerDbSize = query.getPageSize().intValue() / dbCountSTSize;
    int iBegin;
    int iEnd;
    ArrayList dbPerNumList = new ArrayList();
    int iDiffNum = 0; // 当前计算数据库的余量
    int iPageSize = 0; // 每个数据库每页应该显示的记录数
    int iCurrentDBCount = 0;
    int[] isNull = new int[dbCountSTSize]; // 每个数据库是否分配了记录
    int[] iDbCount = new int[dbCountSTSize]; // 数据库的记录累加

    for (int i = 0; i < keys.size(); i++) {
      String tmpDbName = (String) keys.get(i);
      QueryNumber obj = new QueryNumber(tmpDbName, 0, 0);

      iCurrentDBCount = ((Integer) countResult.get(tmpDbName)).intValue() + iCurrentDBCount;
      iDbCount[i] = iCurrentDBCount;

      if (i != 0) {
        if (isNull[i - 1] == 0) { // 前面的数据库已经没有记录可以分配，需要重新计算余下每个数据库每页显示记录数
          iOffSet = query.getPageSize().intValue() % (dbCountSTSize - i);
          iPerDbSize = query.getPageSize().intValue() / (dbCountSTSize - i);
          iDiffNum = 0;
        }
      }

      if (iOffSet > 0) { // 每页显示的记录数不能整除，则从第一个库加一直到0
        iPageSize = iPerDbSize + 1;
        iBegin = ((query.getCurrentPage().intValue() - 1) * iPageSize) + 1;
        iEnd = (iBegin + iPageSize) - 1;

        if (iBegin <= iCurrentDBCount) {
          obj.startNum = iBegin;
          iOffSet--;
        } else {
          iDiffNum = iPerDbSize;
        }
      } else {
        iPageSize = iPerDbSize;
        iBegin = ((query.getCurrentPage().intValue() - 1) * iPageSize) + 1;
        iEnd = (iBegin + iPageSize) - 1;

        if (iBegin <= iCurrentDBCount) {
          obj.startNum = iBegin;
        } else {
          iDiffNum = iPerDbSize;
        }
      }

      iEnd += iDiffNum; // 截至记录数需要要加上偏移量

      if (iEnd <= iCurrentDBCount) {
        if (obj.startNum == 0) {
          obj.endNum = 0;
        } else {
          obj.endNum = iEnd;
          iDiffNum = 0;
        }
      } else {
        if (obj.startNum > 0) {
          obj.endNum = iCurrentDBCount;
          iDiffNum = iEnd - iCurrentDBCount;
        }
      }

      if (obj.endNum > 0) { // 如果有截至记录数，则存入查询列表

        if ((i > 0) && (isNull[i - 1] == 0)) {
          obj.endNum = obj.endNum - iDbCount[i - 1];
          obj.startNum = obj.startNum - iDbCount[i - 1];
        }

        dbPerNumList.add(obj);
        isNull[i] = 1;
      } else {
        isNull[i] = 0;
      }
    }

    // alahan modify end
    // 得到记录集
    dbMap = getSqlMapClient(query.getDbRoute(), selelctStatementName); // select
    // 查询

    int dbSelectSTCount = dbMap.size();

    if (dbSelectSTCount == 0) {
      throw new IllegalStateException("dbroute config error in select statement :" + selelctStatementName);
    }

    if (dbSelectSTCount != dbCountSTSize) {
      throw new IllegalStateException("dbroute config error is differ from select statement and count statement");
    }

    it = dbMap.keySet().iterator();

    List resultList = new ArrayList();

    // alahan modify start
    for (int i = 0; i < dbPerNumList.size(); i++) {
      QueryNumber obj = (QueryNumber) dbPerNumList.get(i);
      DBRoute dbRoute = new DBRoute();

      dbRoute.setXid(obj.xid);
      query.setStartRow(obj.startNum);
      query.setEndRow(obj.endNum);

      List list = this.executeQueryForList(selelctStatementName, query, dbRoute);

      resultList.addAll(list);
    }

    // alahan modify end
    if ((resultList == null) || resultList.isEmpty()) {
      return Collections.EMPTY_LIST;
    }

    // 结果集排序
    Object dataObject = resultList.get(0);

    if (dataObject instanceof Comparable) {
      if (log.isDebugEnabled()) {
        log.debug("the result of statement:" + selelctStatementName + " need to sort.");
      }

      long beginTime = System.currentTimeMillis();

      Collections.sort(resultList);

      long endTime = System.currentTimeMillis();

      if (log.isDebugEnabled()) {
        log.debug("sort time:" + (endTime - beginTime) + "ms");
      }
    }

    return resultList;
  }

  protected List executeQueryForMergeSortList(String statementName, Object parameterObject, int startResult, int maxResults, String orderByMethodString,
      DBRoute dr) throws DAOException {
    Map dbMap = getSqlMapClient(dr, statementName);
    Iterator it = dbMap.keySet().iterator();
    List resultList = new ArrayList();

    if (parameterObject instanceof Map) {
      Map param = (Map) parameterObject;
      param.put("startRow", new Integer(1));
      param.put("endRow", new Integer((maxResults + startResult) - 1));
    }

    // 分别从多库中查找所需的数据
    try {
      while (it.hasNext()) {
        long startTime = System.currentTimeMillis();
        String dbName = (String) it.next();
        SqlMapClientTemplate st = (SqlMapClientTemplate) dbMap.get(dbName);

        // Call spring template
        List list = st.queryForList(statementName, parameterObject);
        long endTime = System.currentTimeMillis();

        logRunTime(statementName, dbName, endTime - startTime);
        genDBRoute(dbName, list);
        resultList.add(list);
      }
    } catch (DataAccessException dae) {
      throw new DAOException(dae);
    }

    // 进行排序
    SqlMapComparator comp = new SqlMapComparator(orderByMethodString);

    try {
      List returnList = comp.sort(resultList, startResult, maxResults);

      if (returnList == null) {
        return new ArrayList();
      }

      return returnList;
    } catch (Exception e) {
      throw new DAOException("排序失败！", e);
    }
  }

  /**
   * 对返回的每条记录设置分库信息
   *
   * @param dbName
   * @param list
   */
  private void genDBRoute(String dbName, List list) {
    // 设置分库信息
    if (list != null) {
      for (int i = 0; i < list.size(); i++) {
        if ((list.get(i) != null) && BaseDO.class.isAssignableFrom(list.get(i).getClass())) {
          ((BaseDO) list.get(i)).setDbRoute(new DBRoute(dbName));
        } else {
          break;
        }
      }
    } // end if
  }

  /**
   * 对返回的每条记录设置分库信息
   *
   * @param dbName
   * @param resultMap
   */
  private void genDBRoute(String dbName, Map resultMap) {
    // 设置分库信息
    if (resultMap != null) {
      Iterator iter = resultMap.values().iterator();

      while (iter.hasNext()) {
        Object element = (Object) iter.next();

        if (BaseDO.class.isAssignableFrom(element.getClass())) {
          ((BaseDO) element).setDbRoute(new DBRoute(dbName));
        } else {
          break;
        }
      }
    } // end if
  }

  /**
   * 根据List中的搜索出来的ID进行分类，然后分别到相关的数据库中搜索出相关的对象. 然后把搜索出来的对象设置到List中Object相应的属性中
   *
   * @param List
   *          list
   * @param String
   *          statementName 搜索语句ID
   * @param String[]
   *          strKeyMethodName ID的方法数组
   * @param String[]
   *          strSetObjectMethodName 设置结果对象的方法数组
   * @param String
   *          strKeyName 搜索SQL语句中的id
   * @param String
   *          strRouteMethodName 分组规则的方法名，如为null取strKeyMethodName做为分组规则的方法名
   *
   * @throws Exception
   * @throws SQLException
   */
  protected void mergeQueryForList(List list, String statementName, String[] strKeyMethodName, String[] strSetObjectMethodName, String strKeyName,
      String strRouteMethodName) throws DAOException {
    if ((list == null) || list.isEmpty()) {
      return;
    }

    Map map = getIdsListAndGroupDB(list, statementName, strKeyMethodName, strRouteMethodName);
    Map idsMap = (Map) map.get("ids");
    Map objMap = (Map) map.get("obj");
    Iterator dbNodes = idsMap.keySet().iterator();

    while (dbNodes.hasNext()) {
      String dbName = (String) dbNodes.next();

      if (dbName == null) {
        continue;
      }

      SqlMapClientTemplate sqlMap = getSqlMapTemplate(new DBRoute(dbName));
      Set param = (Set) idsMap.get(dbName);

      if (param == null) {
        continue;
      }

      Map retMap = new HashMap();
      Iterator it = param.iterator();

      while (it.hasNext()) {
        Object p = it.next();

        if (p == null) {
          continue;
        }

        Map retMap1;

        retMap1 = sqlMap.queryForMap(statementName, p, strKeyName);

        retMap.putAll(retMap1);
      }

      Iterator keys = retMap.keySet().iterator();

      while (keys.hasNext()) {
        Object key = keys.next();
        Object val = retMap.get(key);

        if ((val != null) && BaseDO.class.isAssignableFrom(val.getClass())) {
          ((BaseDO) val).setDbRoute(new DBRoute(dbName));
        }

        for (int i = 0; i < strSetObjectMethodName.length; i++) {
          List objList = (List) objMap.get(key + "_" + i);

          if (objList == null) {
            continue;
          }

          for (int j = 0; j < objList.size(); j++) {
            setProperty(objList.get(j), strSetObjectMethodName[i], val);
          }
        }
      }
    }
  }

  protected List mergeQueryForListByIds(String statementName, String[] ids, String strKeyName) throws DAOException {
    if (ids == null) {
      return null;
    }

    Map idsMap = groupDB(ids, statementName);

    Iterator dbNodes = idsMap.keySet().iterator();
    BaseDO[] rets = null;

    Map retMap = new HashMap();

    while (dbNodes.hasNext()) {
      String dbName = (String) dbNodes.next();

      if (dbName == null) {
        continue;
      }

      SqlMapClientTemplate sqlMap = getSqlMapTemplate(new DBRoute(dbName));
      Set param = (Set) idsMap.get(dbName);

      if (param == null) {
        continue;
      }

      Iterator it = param.iterator();

      while (it.hasNext()) {
        Object p = it.next();

        if (p == null) {
          continue;
        }

        Map retMap1;

        retMap1 = sqlMap.queryForMap(statementName, p, strKeyName);

        retMap.putAll(retMap1);
      }
    }

    Iterator keys = retMap.keySet().iterator();
    rets = new BaseDO[ids.length];

    while (keys.hasNext()) {
      String key = (String) keys.next();
      Object val = retMap.get(key);

      /*
       * if ((val != null) && BaseDO.class.isAssignableFrom(val.getClass())) {
       * ((BaseDO) val).setDbRoute(new DBRoute(dbName)); }
       */

      for (int i = 0; i < ids.length; i++) {
        if (key.equals(ids[i])) {
          rets[i] = (BaseDO) val;
        }
      }
    }

    return Arrays.asList(rets);
  }

  /**
   *
   * @param statementName
   * @param parameterObject
   * @param qualification
   * @param expectedUpdateNumber
   * @return
   * @throws DAOException
   */
  protected int executeUpdateList(String statementName, Object parameterObject, DBRoute qualification, int expectedUpdateNumber) throws DAOException {
    int updateNumber = 0;
    Map dbMap = null;
    Iterator it = null;

    try {
      updateNumber = 0;
      dbMap = getSqlMapClient(qualification, statementName);
      it = dbMap.keySet().iterator();
    } catch (Exception e) {
      throw new DAOException("Failed while executing update.  Maybe for : " + e, e);
    }

    while (it.hasNext()) {
      long startTime = System.currentTimeMillis();
      String dbName = (String) it.next();
      SqlMapClientTemplate sqlMap = (SqlMapClientTemplate) dbMap.get(dbName);

      // updateNumber += sqlMap.executeUpdate(statementName,
      // parameterObject);
      updateNumber += sqlMap.update(statementName, parameterObject);

      long endTime = System.currentTimeMillis();

      logRunTime(statementName, dbName, endTime - startTime);

      if ((expectedUpdateNumber > 0) && (updateNumber >= expectedUpdateNumber)) {
        return updateNumber;
      }
    }

    return updateNumber;
  }

  protected boolean isMviewEnabled() {
    return false;
  }

  /**
   * 把List的ID进行分类。
   *
   * @param List
   *          list
   * @param strKeyMethodName
   *          取搜索ID的方法名
   * @param strRouteMethodName
   *          分类字段方法名
   *
   * @throws DAOException
   */
  private Map getIdsListAndGroupDB(List list, String statementName, String[] strKeyMethodName, String strRouteMethodName) throws DAOException {
    Map ret = new HashMap();
    Map dbMap = new HashMap();
    Map objMap = new HashMap();

    String specifyDbNode = null;

    if (strRouteMethodName != null) {
      if (strRouteMethodName.startsWith("dbNode!")) {
        specifyDbNode = strRouteMethodName.substring("dbNode!".length());
      }
    }

    for (int i = 0; i < list.size(); i++) {
      Object obj = list.get(i);
      String dbNode = specifyDbNode;

      if ((strRouteMethodName != null) && (specifyDbNode == null)) {
        try {
          dbNode = getDbNodeName(getProperty(obj, strRouteMethodName));
        } catch (DAOException e) {
          e.printStackTrace();
        } catch (Exception e) {
          throw new DAOException(e);
        }
      }

      for (int j = 0; j < strKeyMethodName.length; j++) {
        String value;

        try {
          value = getProperty(obj, strKeyMethodName[j]);
        } catch (Exception e) {
          throw new DAOException(e);
        }

        if ((value == null) || (value.length() == 0)) {
          continue;
        }

        if (strRouteMethodName == null) {
          dbNode = getDbNodeName(value);
        }

        List idsList = null;

        if (dbMap.containsKey(dbNode)) {
          idsList = (List) dbMap.get(dbNode);
        } else {
          idsList = new ArrayList();
          dbMap.put(dbNode, idsList);
        }

        List objList = null;
        String strKey = value + "_" + j;

        if (objMap.containsKey(strKey)) {
          objList = (List) objMap.get(strKey);
        } else {
          objList = new ArrayList();
          objMap.put(strKey, objList);
          idsList.add(value);
        }

        objList.add(obj);
      }
    }

    Map idsMap = new HashMap();

    if ((specifyDbNode != null) && (ALL_DEFAULT_DB_NAME.compareTo(specifyDbNode) == 0)) {
      Map map = getSqlMapClient(null, statementName);
      Iterator dbNodes = map.keySet().iterator();
      List ll = (List) dbMap.get(ALL_DEFAULT_DB_NAME);

      Set ids = new HashSet();

      if ((ll != null) && (ll.size() > MAX_IDS_SIZE)) {
        int start = 0;
        int size = ll.size();

        while (true) {
          if (size < MAX_IDS_SIZE) {
            StringBuffer bf = new StringBuffer();

            for (int idx = start; idx < ll.size(); idx++) {
              if (bf.length() > 0) {
                bf.append(",");
              }

              bf.append((String) ll.get(idx));
            }

            ids.add(bf.toString());

            break;
          } else {
            StringBuffer bf = new StringBuffer();

            for (int idx = start; idx < (start + MAX_IDS_SIZE); idx++) {
              if (bf.length() > 0) {
                bf.append(",");
              }

              bf.append((String) ll.get(idx));
            }

            start += MAX_IDS_SIZE;
            size -= MAX_IDS_SIZE;
            ids.add(bf.toString());
          }
        }
      } else {
        ids.add((ll == null) ? null : StringUtil.join(ll.iterator(), ','));
      }

      while (dbNodes.hasNext()) {
        idsMap.put((String) dbNodes.next(), ids);
      }
    } else {
      Iterator dbNodes = dbMap.keySet().iterator();

      while (dbNodes.hasNext()) {
        String key = (String) dbNodes.next();
        List ll = (List) dbMap.get(key);

        Set ids = new HashSet();

        if ((ll != null) && (ll.size() > MAX_IDS_SIZE)) {
          int start = 0;
          int size = ll.size();

          while (true) {
            if (size < MAX_IDS_SIZE) {
              StringBuffer bf = new StringBuffer();

              for (int idx = start; idx < ll.size(); idx++) {
                if (bf.length() > 0) {
                  bf.append(",");
                }

                bf.append((String) ll.get(idx));
              }

              ids.add(bf.toString());

              break;
            } else {
              StringBuffer bf = new StringBuffer();

              for (int idx = start; idx < (start + MAX_IDS_SIZE); idx++) {
                if (bf.length() > 0) {
                  bf.append(",");
                }

                bf.append((String) ll.get(idx));
              }

              start += MAX_IDS_SIZE;
              size -= MAX_IDS_SIZE;
              ids.add(bf.toString());
            }
          }
        } else {
          ids.add((ll == null) ? null : StringUtil.join(ll.iterator(), ','));
        }

        idsMap.put(key, ids);
      }
    }

    ret.put("ids", idsMap);
    ret.put("obj", objMap);

    return ret;
  }

  private Map groupDB(String[] idArray, String statementName) throws DAOException {
    Map dbMap = new HashMap();

    String specifyDbNode = null;

    for (int i = 0; i < idArray.length; i++) {
      String dbNode = specifyDbNode;

      String value = idArray[i];

      if ((value == null) || (value.length() == 0)) {
        continue;
      }

      dbNode = getDbNodeName(value);

      List idsList = null;

      if (dbMap.containsKey(dbNode)) {
        idsList = (List) dbMap.get(dbNode);
      } else {
        idsList = new ArrayList();
        dbMap.put(dbNode, idsList);
      }

      if (!idsList.contains(value)) {
        idsList.add(value);
      }
    }

    Map idsMap = new HashMap();

    if ((specifyDbNode != null) && (ALL_DEFAULT_DB_NAME.compareTo(specifyDbNode) == 0)) {
      Map map = getSqlMapClient(null, statementName);
      Iterator dbNodes = map.keySet().iterator();
      List ll = (List) dbMap.get(ALL_DEFAULT_DB_NAME);

      Set ids = new HashSet();

      if ((ll != null) && (ll.size() > MAX_IDS_SIZE)) {
        int start = 0;
        int size = ll.size();

        while (true) {
          if (size < MAX_IDS_SIZE) {
            StringBuffer bf = new StringBuffer();

            for (int idx = start; idx < ll.size(); idx++) {
              if (bf.length() > 0) {
                bf.append(",");
              }

              bf.append((String) ll.get(idx));
            }

            ids.add(bf.toString());

            break;
          } else {
            StringBuffer bf = new StringBuffer();

            for (int idx = start; idx < (start + MAX_IDS_SIZE); idx++) {
              if (bf.length() > 0) {
                bf.append(",");
              }

              bf.append((String) ll.get(idx));
            }

            start += MAX_IDS_SIZE;
            size -= MAX_IDS_SIZE;
            ids.add(bf.toString());
          }
        }
      } else {
        ids.add((ll == null) ? null : StringUtil.join(ll.iterator(), ','));
      }

      while (dbNodes.hasNext()) {
        idsMap.put((String) dbNodes.next(), ids);
      }
    } else {
      Iterator dbNodes = dbMap.keySet().iterator();

      while (dbNodes.hasNext()) {
        String key = (String) dbNodes.next();
        List ll = (List) dbMap.get(key);

        Set ids = new HashSet();

        if ((ll != null) && (ll.size() > MAX_IDS_SIZE)) {
          int start = 0;
          int size = ll.size();

          while (true) {
            if (size < MAX_IDS_SIZE) {
              StringBuffer bf = new StringBuffer();

              for (int idx = start; idx < ll.size(); idx++) {
                if (bf.length() > 0) {
                  bf.append(",");
                }

                bf.append((String) ll.get(idx));
              }

              ids.add(bf.toString());

              break;
            } else {
              StringBuffer bf = new StringBuffer();

              for (int idx = start; idx < (start + MAX_IDS_SIZE); idx++) {
                if (bf.length() > 0) {
                  bf.append(",");
                }

                bf.append((String) ll.get(idx));
              }

              start += MAX_IDS_SIZE;
              size -= MAX_IDS_SIZE;
              ids.add(bf.toString());
            }
          }
        } else {
          ids.add((ll == null) ? null : StringUtil.join(ll.iterator(), ','));
        }

        idsMap.put(key, ids);
      }
    }

    return idsMap;
  }

  /**
   * 从Object中用methodName方法取得一个值
   *
   * @param Object
   *          object 对象
   * @param String
   *          methodName 方法名
   *
   * @return String 值
   */
  private String getProperty(Object object, String methodName) throws Exception {
    Class claz = object.getClass();
    Method method = claz.getMethod(methodName, new Class[0]);
    Object value = method.invoke(object, NO_ARGUMENTS);

    return ((value == null) ? null : value.toString());
  }

  /**
   * 用methodName设置value到object中
   *
   * @param Object
   *          object 对象
   * @param String
   *          methodName 方法名
   * @param Object
   *          value 值
   */
  private void setProperty(Object object, String methodName, Object value) throws DAOException {
    if ((value == null) || (object == null) || (methodName == null)) {
      return;
    }

    try {
      Class claz = object.getClass();
      Class[] paramType = new Class[1];

      paramType[0] = value.getClass();

      Method method = claz.getMethod(methodName, paramType);
      Object[] params = new Object[1];

      params[0] = value;
      method.invoke(object, params);
    } catch (Exception e) {
      throw new DAOException(e);
    }
  }

  /**
   * 取得节点的名字
   *
   * @return
   *
   * @throws DAOException
   */
  public String getDbNodeName(String node) throws DAOException {
    DBRoute dr = new DBRoute();

    dr.setUserId(node);

    Map map = getSqlMapClient(dr, null);

    if (map != null) {
      Iterator it = map.keySet().iterator();

      while (it.hasNext()) {
        String element = (String) it.next();

        return element;
      }
    }

    return null;
  }

  class QueryNumber {
    public String xid;
    public int    startNum;
    public int    endNum;

    public QueryNumber(String xid, int startNum, int endNum) {
      this.xid = xid;
      this.startNum = startNum;
      this.endNum = endNum;
    }
  }

  /**
   * wanjian 2006.03.10
   * 
   * @param selelctStatementName
   * @param countStatementName
   * @param query
   * @return List
   * @throws DAOException
   * 
   *           在已知DBRoute的情况下，查询一个库的数据
   */
  protected List getQueryListOfOneDB(String selelctStatementName, String countStatementName, QueryBase query) throws DAOException {
    if (selelctStatementName == null) {
      throw new NullPointerException("selelctStatementName can't be null.");
    }

    if (countStatementName == null) {
      throw new NullPointerException("countStatementName can't be null.");
    }

    if (query == null) {
      throw new NullPointerException("query can't be null.");
    }

    Integer iCount = (Integer) this.executeQueryForObject(countStatementName, query, query.getDbRoute());
    query.setTotalItem(iCount);
    return this.executeQueryForList(selelctStatementName, query, query.getDbRoute());
  }

  /**
   * 插入或更新一条记录
   * 
   * @param countStatementName
   * @param insertStatementName
   * @param updateStatementName
   * @param parameterObject
   * @param dr
   * @return Object
   * @throws DAOException
   */
  protected Object executeInsertOrUpdate(String countStatementName, String insertStatementName, String updateStatementName, Object parameterObject, DBRoute dr)
      throws DAOException {
    Integer count = (Integer) this.executeQueryForObject(countStatementName, parameterObject, dr);
    if (null != count && count.intValue() > 0) {
      int u = this.executeUpdate(updateStatementName, parameterObject, dr);
      return new Integer(u);
    }
    return this.executeInsert(insertStatementName, parameterObject, dr);
  }

}
